package com.hk.core.autoconfigure.web;


import com.hk.commons.JsonResult;
import com.hk.commons.util.PropertyAccessorUtils;
import com.hk.commons.util.ProxyUtils;
import com.hk.commons.util.SpringContextHolder;
import com.hk.commons.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.*;

import java.util.Objects;

/**
 * 有时候，你的项目上线了，等几天后却发现，你的配置不对，此时，怎么办？重启，这成本太大了，生产哪能你说重启就重启？？
 * 这里 {@link  #refresh(String, String, String, String, String)} 方法示例，怎么修改bean中的属性，
 * 注意，此种方法只是解决了引用对象的问题
 * 如果你的环境变量中的属性，赋值给别的多个对象后，别的对象已不再引用这个对象时，你需要根据代码，一一查找赋值后的对象，重新赋值
 * 修改Bean中的属性
 * <p>
 * 注意： 这种修改并没有修改掉 {@link  org.springframework.core.env.Environment} 对象中的属性值
 *
 * @author Kevin
 */
@Slf4j
@RestController
@RequestMapping("internal")
public class RefreshBeanPropertyController {

    private static final String HEADER_NAME = "rbpc"; //RefreshBeanPropertyController

    /**
     * 随机验证头，只能使用一次，使用后重新生成新值
     */
    private volatile String randomHeaderValue;

    public RefreshBeanPropertyController() {
        generateNewValue();
    }

    private void generateNewValue() {
        randomHeaderValue = RandomStringUtils.randomAlphanumeric(RandomUtils.nextInt(10, 20));
    }

    /**
     * 修改bean的属性
     *
     * @param token     请求头，加验证使用
     * @param property  要修改的对象属性值
     * @param beanType  要修改的对象类名
     * @param newValue  修改的新值
     * @param valueType 修改的新值类型名，如 java.lang.String /java.lang.Integer
     */
    @PostMapping("refresh")
    public JsonResult<?> refresh(@RequestHeader(value = HEADER_NAME) String token, @RequestParam String property,
                                 @RequestParam String beanType, @RequestParam String newValue,
                                 @RequestParam String valueType) {
        if (StringUtils.notEquals(token, randomHeaderValue)) {
            printHeaderValue();
            return JsonResult.unauthorized("非法请求");
        }

        // 修改的bean对象类名必须是 com.hk开头的，禁止修改数据库、redis等这种核心配置
        if (!StringUtils.startsWith(beanType, "com.hk")) {
            return JsonResult.failure("非法请求");
        }
        try {
            var valueClazz = Class.forName(valueType);
            if (BeanUtils.isSimpleValueType(valueClazz)) {
                var beanClazz = Class.forName(beanType);
                var object = SpringContextHolder.getBean(beanClazz);
                var target = ProxyUtils.getTargetObject(object);
                //直接修改对象属性，不需要set方法，如果使用 BeanWrapper，需要有set/get方法
                var fieldAccessor = PropertyAccessorUtils.forDirectFieldAccess(target);
                var oldValue = fieldAccessor.getPropertyValue(property);
                fieldAccessor.setPropertyValue(property, newValue);
                if (log.isWarnEnabled()) {
                    log.warn("Object property value is modified: objName:{},property:{},oldValue:{},newValue:{}",
                            beanType, property, oldValue, newValue);
                }
                return JsonResult.success(null);
            }
            return JsonResult.failure("error.");
        } catch (Exception e) {
            return JsonResult.failure(e.getMessage());
        } finally {
            generateNewValue();
        }
    }

    private void printHeaderValue() {
        if (log.isWarnEnabled()) {
            log.warn("name:{}", randomHeaderValue);
        } else {
            System.out.printf("""
                    -----------------------------------------------
                    value: %s
                    -----------------------------------------------\
                    """, randomHeaderValue);
        }
    }

    /**
     * 获取对象属性值
     *
     * @param token    token
     * @param beanType beanClassName
     * @param property bean property
     */
    @PostMapping("get")
    public JsonResult<?> getBeanInfo(@RequestHeader(value = HEADER_NAME) String token, @RequestParam String beanType,
                                     @RequestParam(required = false) String property) {
        if (StringUtils.notEquals(token, randomHeaderValue)) {
            printHeaderValue();
            return JsonResult.unauthorized("非法请求");
        }
        // 查看的bean对象类名必须是 com.hk开头的，禁止查看数据库、redis等这种核心配置
        if (!StringUtils.startsWith(beanType, "com.hk")) {
            return JsonResult.failure("非法请求");
        }
        try {
            var clazz = Class.forName(beanType);
            var obj = SpringContextHolder.getBean(clazz);
            var target = ProxyUtils.getTargetObject(obj);
            if (StringUtils.hasLength(property)) {
                var field = ReflectionUtils.findField(clazz, property);
                if (Objects.nonNull(field)) {
                    ReflectionUtils.makeAccessible(field);
                    var value = ReflectionUtils.getField(field, target);
                    return JsonResult.success(value);
                }
                return JsonResult.failure();
            }
            return JsonResult.success(target);
        } catch (Exception e) {
            return JsonResult.failure(e.getMessage());
        } finally {
            generateNewValue();
        }
    }


}
